#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include <limits>
#include <algorithm>
using namespace std;

#include "common.h"
#include "scene.h"
#include "image.h"

namespace RAYTRACER
{


	Color raytrace (Ray & ray, scene & myScene) 
	{
		Color color (0.0f, 0.0f, 0.0f);
		float coef = 1.0f;
		int level = 0; 
        do 
        { 
            // Looking for the closest intersection : newStart, and the normal n ;
            int currentSphere= -1;
			int materialID = -1 ;
			Point3 newStart ;
			Vector3 normal ;

			if (! myScene.intersect(ray, newStart, normal, currentSphere, materialID))
				break;
            material currentMat = myScene.materialContainer[materialID]; 

            // Compute the color of the intersection point ;
            for (unsigned int j = 0; j < myScene.lightContainer.size(); ++j) 
			{
                light current = myScene.lightContainer[j];
                Vector3 dist = current.pos - newStart;
                if (normal * dist <= 0.0f)
                    continue;
                float t = dist.length();
                if ( t <= 0.0f )
                    continue;
                Ray lightRay (newStart, dist.normal());

				// Compute fLightProjection for Blinn
				float fLightProjection = lightRay.dir * normal ;
				if (fLightProjection <= 0.0f) continue ;
				float fLigthDirLength = lightRay.dir.length();
				if (fLigthDirLength <= 0.0f) continue ;
				fLightProjection = 1.0f / fLigthDirLength * fLightProjection ;
				
                // computation of the shadows
                bool inShadow = false; 
                for (unsigned int i = 0; i < myScene.sphereContainer.size(); ++i) 
				{
					if (myScene.sphereContainer[i].intersect(lightRay,  t)) 
					{
                        inShadow = true;
                        break;
                    }
                }
                if (!inShadow) {
                    // lambert Model 
                    float lambert = (lightRay.dir * normal) * coef;
					color += lambert * current.color * currentMat.diffuse ;
					
					//Blinn Phong computation
					float fViewProjection = ray.dir * normal ;
					Vector3 blinnDir = lightRay.dir - ray.dir; 
					float blinnDirLen = blinnDir.length();
					if ( blinnDirLen > 0.0f)
					{
						float blinn = 1.0f / blinnDirLen * max (fLightProjection - fViewProjection , 0.0f);
						blinn = coef * pow (blinn, currentMat.power);
						color += blinn * currentMat.specular * current.color ;
					}

                }
            }

            // Tracing the reflection light
            coef *= currentMat.reflection;
            float reflet = 2.0f * (ray.dir * normal);
            ray.start = newStart;
            ray.dir = ray.dir - reflet * normal;
            level++;
        } 
        while ((coef > 0.0f) && (level < 5));   
		
		return color ;
		//return color.getGammaCorrection() ;
	}
	bool scan(char* outputName, scene &myScene) 
	{
		TGA tga (outputName);
		tga.writeHeadUnCompressed(myScene.sizex, myScene.sizey);

		// Scanning 
		for (int y = 0; y < myScene.sizey; ++y) 
		{ 
			for (int x = 0; x < myScene.sizex; ++x) 
			{
				Ray viewRay ( Point3(float(x), float(y), -1000.0f), Vector3( 0.0f, 0.0f, 1.0f));
				Color color = raytrace(viewRay, myScene);
				tga.writePixel(color);
			}
		}
		return true;
	}

}

int main(int argc, char* argv[])
{
    if (argc < 2)
    {
        cout << "Usage : "<<argv[0]<<" filename.tga" << endl;
        return -1;
    }
	RAYTRACER::scene myScene;
    if (!init(myScene))
    {
        cout << "Failure when reading the Scene file." << endl;
        return -1;
    }
    if (!RAYTRACER::scan(argv[1], myScene))
    {
        cout << "Failure when creating the image file." << endl;
        return -1;
    }
    return 0;
}
